package com.zyh.scs.admin.service.jd;

import cn.hutool.core.util.StrUtil;
import cn.hutool.http.ContentType;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.zyh.scs.admin.api.bo.JdAfterSaleQueryP;
import com.zyh.scs.admin.api.bo.JdAfterSaleR;
import com.zyh.scs.admin.api.bo.JdOrderQueryP;
import com.zyh.scs.admin.api.bo.JdQueryOrderR;
import com.zyh.scs.admin.api.dto.jd.RefundOrder;
import com.zyh.scs.admin.api.entity.jd.*;
import com.zyh.scs.admin.base.jd.QueryJDResult;
import com.zyh.scs.admin.base.jd.QueryRefundParam;
import com.zyh.scs.admin.base.jd.QueryResponse;
import com.zyh.scs.admin.utils.jd.RsaCoder;
import com.zyh.scs.admin.utils.jd.ShaUtil;
import com.zyh.scs.admin.utils.jd.SignatureUtil;
import com.zyh.scs.common.core.jackson.ScsJavaTimeModule;
import com.zyh.scs.common.core.util.BeanCopyUtil;
import lombok.Cleanup;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.nio.charset.StandardCharsets;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
@RequiredArgsConstructor
public class JdBatchComponent {
    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss");
    private static final ObjectMapper objectMapper = new ObjectMapper();

    static {
        objectMapper.registerModule(new ScsJavaTimeModule());
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
    }

    private final JdRefundOrderService jdRefundOrderService;
    private final JdRefundOrderSkuService jdRefundOrderSkuService;
    private final JdOrderService jdOrderService;
    private final JdAfterSaleService jdAfterSaleService;
    private final JdOrderSkuService jdOrderSkuService;
    /**
     * 京东BPIIN
     */
    @Value("${biz.jd.bpin}")
    public String BPIN;
    /**
     * 京东accessKey
     */
    @Value("${biz.jd.accessKey}")
    public String ACCESS_KEY;
    /**
     * 京东私钥
     */
    @Value("${biz.jd.rsaSignPrivateKey}")
    public String RSA_SIGN_PRIVATE_KEY;
    /**
     * 京东公钥
     */
    @Value("${biz.jd.rsaEncryptPublicKey}")
    public String RSA_ENCRYPT_PUBLIC_KEY;
    /**
     * 查询退款订单
     */
    @Value("${biz.jd.refundOrderUrl}")
    public String REFUND_ORDER_URL;

    @Value("${biz.jd.queryOrderUrl}")
    public String JD_QUERY_ORDER_URL;

    @Value("${biz.jd.queryAfterSaleUrl}")
    public String JD_AFTER_SALE_URL;

    /**
     * redis客户端
     */
    private final RedissonClient redissonClient;

    private final String USER_TASK_KEY = "USER_TASK_KEY";

    /**
     * 拉取jd订单
     * 附带录入商品信息
     */
    @SneakyThrows
    public void pullOrdersFromJd(String timeNow) {

        //加redis 定位任务锁
        RLock lock = redissonClient.getLock(USER_TASK_KEY + "pullOrdersFromJd");

        try {

            lock.lock(10, TimeUnit.SECONDS);

            LocalDate now = LocalDate.now();
            if (StrUtil.isNotBlank(timeNow)) {
                now = getFirstDayOfMonth(timeNow);
            }
            log.info("开始拉取jd订单，时间：{}", now);
            final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss");
            final LocalDateTime startDayOfLastMonth = now.minusMonths(1L).with(TemporalAdjusters.firstDayOfMonth()).atStartOfDay();
            final LocalDateTime endDayOfLastMonth = now.minusMonths(1L).with(TemporalAdjusters.lastDayOfMonth()).atTime(LocalTime.MAX);

            final String startTime = startDayOfLastMonth.format(dtf);
            final String endTime = endDayOfLastMonth.format(dtf);

            log.info("拉取jd订单 startTime: {}, endTime: {}", startTime, endTime);
            int pageNo = 1;
            int pageSize = 200;
            while (true) {
                JdOrderQueryP orderQueryParams = new JdOrderQueryP();
                orderQueryParams.setStartTime(startTime);
                orderQueryParams.setEndTime(endTime);
                orderQueryParams.setAccessKey(ACCESS_KEY);
                orderQueryParams.setTimestamp(Instant.now().toEpochMilli());
                orderQueryParams.setPageSize(pageSize);
                orderQueryParams.setPageNo(pageNo);
                //以完成时间为准
                orderQueryParams.setTimeType("2");

                //sign验证 //这两个参数不参与签名
                List<String> unsignedKeyList = new ArrayList<>();
                unsignedKeyList.add("version");
                unsignedKeyList.add("sign");
                // 获取验签sign
                // 1.将参数按照ASCII码从小到大的顺序排列并使用&符号拼接-s1
                String s1 = SignatureUtil.signString(orderQueryParams, unsignedKeyList);
                // 2.将s1使用"SHA-256"加密方式进行加密获取字符串-s2
                String s2 = ShaUtil.encrypt(s1, "SHA-256");
                if (!StringUtils.hasText(s2)) {
                    log.error("拉取jd订单接口，SHA-256加签失败");
                    break;
                }
                byte[] s2Rsa = RsaCoder.encryptByPrivateKey(s2.getBytes(StandardCharsets.UTF_8), RSA_SIGN_PRIVATE_KEY);
                // 4.将s3进行base64进行编码
                String sign = RsaCoder.encryptBASE64(s2Rsa);
                orderQueryParams.setSign(sign);
                log.info("查询参数{}", objectMapper.writeValueAsString(orderQueryParams));

                @Cleanup final HttpResponse execute = HttpRequest.post(JD_QUERY_ORDER_URL).contentType(ContentType.FORM_URLENCODED.getValue()).form(objectMapper.convertValue(orderQueryParams, new TypeReference<>() {
                })).execute();
                final String body = execute.body();
                log.info("定时任务京东订单接口返回报文：{}", body);
                final JdQueryOrderR jdOrderR = objectMapper.readValue(body, JdQueryOrderR.class);
                final List<JdQueryOrderR.OrderData> orderData = Optional.ofNullable(jdOrderR).map(JdQueryOrderR::getResult).map(JdQueryOrderR.OrderResult::getDatas).orElse(List.of());
                if (orderData.isEmpty()) {
                    break;
                }
                final Set<JdOrder> shallSaveJdOrders = new ConcurrentSkipListSet<>(Comparator.comparing(JdOrder::getJdOrderId));
                //sku信息毋须更新
                final Set<JdOrderSku> shallSaveJdOrderSkus = new ConcurrentSkipListSet<>(Comparator.comparing(JdOrderSku::getSkuId));
                //do something with orderData
                orderData.parallelStream().forEach(x -> {
                    if (jdOrderService.lambdaQuery().eq(JdOrder::getJdOrderId, x.getJdOrderId()).eq(JdOrder::getDelFlag, Boolean.FALSE).exists()) {
                        return;
                    }
                    final List<JdQueryOrderR.Sku> skuList = x.getSkuList();
                    final List<JdOrderSku> jdOrderSkusOfOrder = BeanCopyUtil.copyListToList(skuList, JdOrderSku.class);
                    final List<JdOrderSku> existSkuList = jdOrderSkuService.lambdaQuery().eq(JdOrderSku::getOfOrderId, x.getJdOrderId()).eq(JdOrderSku::getDelFlag, Boolean.FALSE).list();
                    jdOrderSkusOfOrder.removeIf(j -> existSkuList.stream().anyMatch(s -> ObjectUtils.nullSafeEquals(s.getSkuId(), j.getSkuId())));
                    jdOrderSkusOfOrder.forEach(j -> {
                        j.setOfOrderId(x.getJdOrderId());
                        j.setCreateBy("timing_job");
                        j.setUpdateBy("timing_job");
                        final LocalDateTime current = LocalDateTime.now();
                        j.setCreateTime(current);
                        j.setUpdateTime(current);
                    });
                    shallSaveJdOrderSkus.addAll(jdOrderSkusOfOrder);
                    final JdOrder jdOrder = BeanCopyUtil.copy(x, JdOrder.class);
                    shallSaveJdOrders.add(jdOrder);
                });
                jdOrderService.saveBatch(shallSaveJdOrders);
                log.info("拉取jdOrderService京东订单信息成功，共{}条, 拉取京东订单信息成功数据: {}", shallSaveJdOrders.size(), JSONArray.toJSONString(shallSaveJdOrders));
                jdOrderSkuService.saveBatch(shallSaveJdOrderSkus);
                log.info("拉取jdOrderSkuService京东订单信息成功，共{}条, 拉取京东订单信息成功数据: {}", shallSaveJdOrderSkus.size(), JSONArray.toJSONString(shallSaveJdOrderSkus));
                pageNo++;
            }

        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /**
     * 拉取jd退款信息
     */
    @SneakyThrows
    public void pullRefundOrderFromJd(String timeNow) {

        //加redis 定位任务锁
        RLock lock = redissonClient.getLock(USER_TASK_KEY + "pullRefundOrderFromJd");

        try {

            lock.lock(10, TimeUnit.SECONDS);

            LocalDate now = LocalDate.now();
            if (StrUtil.isNotBlank(timeNow)) {
                now = getFirstDayOfMonth(timeNow);
            }
            log.info("开始拉取jd退款信息，时间：{}", now);
            final LocalDateTime startDayOfLastMonth = now.minusMonths(1L).with(TemporalAdjusters.firstDayOfMonth()).atStartOfDay();
            final LocalDateTime endDayOfLastMonth = now.minusMonths(1L).with(TemporalAdjusters.lastDayOfMonth()).atTime(LocalTime.MAX);
            String startDate = startDayOfLastMonth.format(DATE_TIME_FORMATTER);
            String endDate = endDayOfLastMonth.format(DATE_TIME_FORMATTER);
            log.info("拉取jd退款信息 afsApplyTimeBegin: {}, afsApplyTimeEnd: {}", startDate, endDate);
            int pageSize = 200;
            int pageNum = 1;
            while (true) {
                QueryRefundParam queryRefundParam = new QueryRefundParam();
                queryRefundParam.setAccessKey(ACCESS_KEY);
                Long timestamp = System.currentTimeMillis();
                queryRefundParam.setTimestamp(timestamp + "");
                queryRefundParam.setPageSize(String.valueOf(pageSize));
                queryRefundParam.setPageNum(String.valueOf(pageNum));
                queryRefundParam.setStartDate(startDate);
                queryRefundParam.setEndDate(endDate);

                //sign验证 //这两个参数不参与签名
                List<String> unsignedKeyList = new ArrayList<>();
                unsignedKeyList.add("version");
                unsignedKeyList.add("sign");
                // 获取验签sign
                // 1.将参数按照ASCII码从小到大的顺序排列并使用&符号拼接-s1
                String s1 = SignatureUtil.signString(queryRefundParam, unsignedKeyList);
                ;
                // 2.将s1使用"SHA-256"加密方式进行加密获取字符串-s2
                String s2 = ShaUtil.encrypt(s1, "SHA-256");
                // 3.将s2再使用验签私钥进行加密成为s3
                if (!StringUtils.hasText(s2)) {
                    log.error("拉取jd退款订单接口，SHA-256加签失败");
                    break;
                }
                byte[] s2Rsa = RsaCoder.encryptByPrivateKey(s2.getBytes(StandardCharsets.UTF_8), RSA_SIGN_PRIVATE_KEY);
                // 4.将s3进行base64进行编码
                String sign = RsaCoder.encryptBASE64(s2Rsa);
                queryRefundParam.setSign(sign);
                log.info("查询参数{}", JSON.toJSONString(queryRefundParam));

                @Cleanup final HttpResponse execute = HttpRequest.post(REFUND_ORDER_URL)
                        .form(objectMapper.convertValue(queryRefundParam, new TypeReference<>() {
                        })).contentType(ContentType.FORM_URLENCODED.getValue()).execute();
                final String body = execute.body();
                log.info("定时任务京东查询退款订单接口返回报文：{}", body);
                final QueryResponse queryResponse = objectMapper.readValue(body, QueryResponse.class);
                final List<RefundOrder> res = Optional.ofNullable(queryResponse).map(QueryResponse::getResult).map(
                        QueryJDResult::getData).orElse(List.of());
                if (res.isEmpty()) {
                    break;
                }
                final Set<JdRefundOrder> shallSaveJdRefundOrders = new ConcurrentSkipListSet<>(Comparator.comparing(JdRefundOrder::getId));
                final Set<JdRefundOrderSku> shallSaveJdRefundOrderSkus = new ConcurrentSkipListSet<>(Comparator.comparing(JdRefundOrderSku::getSkuId));
                res.parallelStream().forEach(r -> {
                    final JdRefundOrder j = BeanCopyUtil.copy(r, JdRefundOrder.class);
                    j.setId(Long.parseLong(r.getId()));
                    if (jdRefundOrderService.lambdaQuery().eq(JdRefundOrder::getId, j.getId()).eq(JdRefundOrder::getDelFlag, Boolean.FALSE).exists()) {
                        return;
                    }
                    Optional.ofNullable(r.getAfsServiceId()).ifPresent(a -> j.setAfsServiceId(Long.parseLong(a)));
                    Optional.ofNullable(r.getRefundOrderId()).ifPresent(rfi -> j.setRefundOrderId(Long.parseLong(rfi)));
                    j.setCreateDate(r.getCreateDate().getTime());
                    j.setUpdateDate(r.getUpdateDate().getTime());
                    j.setCreateBy("timing_job");
                    j.setUpdateBy("timing_job");
                    final LocalDateTime current = LocalDateTime.now();
                    j.setCreateTime(current);
                    j.setUpdateTime(current);
                    shallSaveJdRefundOrders.add(j);
                    final List<RefundOrder.SkuInfo> skuInfo = r.getSkuInfo();
                    skuInfo.forEach(s -> {
                        final JdRefundOrderSku jros = BeanCopyUtil.copy(s, JdRefundOrderSku.class);
                        jros.setOfRefundId(j.getId());
                        jros.setCreateBy("timing_job");
                        jros.setUpdateBy("timing_job");
                        jros.setCreateTime(current);
                        jros.setUpdateTime(current);
                        shallSaveJdRefundOrderSkus.add(jros);
                    });
                });
                jdRefundOrderService.saveBatch(shallSaveJdRefundOrders);
                log.info("拉取jdRefundOrderService退款京东订单信息成功，共{}条, 拉取退款京东订单信息成功: {}", shallSaveJdRefundOrders.size(), JSONArray.toJSONString(shallSaveJdRefundOrders));
                jdRefundOrderSkuService.saveBatch(shallSaveJdRefundOrderSkus);
                log.info("拉取jdRefundOrderSkuService退款京东订单信息成功，共{}条, 拉取退款京东订单信息成功: {}", shallSaveJdRefundOrderSkus.size(), JSONArray.toJSONString(shallSaveJdRefundOrderSkus));
                pageNum++;
            }
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /**
     * 拉去jd售后单信息
     */
    @SneakyThrows
    public void pullAfterServiceOrder(String timeNow) {

        //加redis 定位任务锁
        RLock lock = redissonClient.getLock(USER_TASK_KEY + "pullAfterServiceOrder");

        try {

            lock.lock(10, TimeUnit.SECONDS);

            LocalDate now = LocalDate.now();
            if (StrUtil.isNotBlank(timeNow)) {
                now = getFirstDayOfMonth(timeNow);
            }
            log.info("开始拉取jd售后单信息，时间：{}", now);
            final LocalDateTime startDayOfLastMonth = now.minusMonths(1L).with(TemporalAdjusters.firstDayOfMonth()).atStartOfDay();
            final LocalDateTime endDayOfLastMonth = now.minusMonths(1L).with(TemporalAdjusters.lastDayOfMonth()).atTime(LocalTime.MAX);
            String afsApplyTimeBegin = startDayOfLastMonth.format(DATE_TIME_FORMATTER);
            String afsApplyTimeEnd = endDayOfLastMonth.format(DATE_TIME_FORMATTER);

            log.info("拉去jd售后单信息 afsApplyTimeBegin: {}, afsApplyTimeEnd: {}", afsApplyTimeBegin, afsApplyTimeEnd);
            int pageSize = 20;
            int pageNo = 1;

            while (true) {
                JdAfterSaleQueryP jdAfterSaleQueryP = new JdAfterSaleQueryP();

                Long timestamp = System.currentTimeMillis();
                jdAfterSaleQueryP.setTimestamp(timestamp);
                jdAfterSaleQueryP.setAccessKey(ACCESS_KEY);
                jdAfterSaleQueryP.setPageSize(pageSize);
                jdAfterSaleQueryP.setPageNo(pageNo);
                jdAfterSaleQueryP.setAfsApplyTimeBegin(afsApplyTimeBegin);
                jdAfterSaleQueryP.setAfsApplyTimeEnd(afsApplyTimeEnd);

                //sign验证 //这两个参数不参与签名
                List<String> unsignedKeyList = new ArrayList<>();
                unsignedKeyList.add("version");
                unsignedKeyList.add("sign");
                // 获取验签sign
                // 1.将参数按照ASCII码从小到大的顺序排列并使用&符号拼接-s1
                String s1 = SignatureUtil.signString(jdAfterSaleQueryP, unsignedKeyList);
                // 2.将s1使用"SHA-256"加密方式进行加密获取字符串-s2
                String s2 = ShaUtil.encrypt(s1, "SHA-256");
                // 3.将s2再使用验签私钥进行加密成为s3
                if (!StringUtils.hasText(s2)) {
                    log.error("SHA-256加签失败");
                    break;
                }
                byte[] s2Rsa = RsaCoder.encryptByPrivateKey(s2.getBytes(StandardCharsets.UTF_8), RSA_SIGN_PRIVATE_KEY);
                // 4.将s3进行base64进行编码
                String sign = RsaCoder.encryptBASE64(s2Rsa);
                jdAfterSaleQueryP.setSign(sign);
                log.info("查询参数{}", objectMapper.writeValueAsString(jdAfterSaleQueryP));

                @Cleanup final HttpResponse execute = HttpRequest.post(JD_AFTER_SALE_URL)
                        .form(objectMapper.convertValue(jdAfterSaleQueryP, new TypeReference<>() {
                        })).contentType(ContentType.FORM_URLENCODED.getValue()).execute();
                final String body = execute.body();
                log.info("定时任务京东查询售后接口返回报文：{}", body);

                final JdAfterSaleR jdAfterSaleR = objectMapper.readValue(body, JdAfterSaleR.class);
                final List<JdAfterSaleR.JdAfterSaleService> jdAfterSaleServices = Optional.ofNullable(jdAfterSaleR).map(JdAfterSaleR::getResult).map(JdAfterSaleR.Result::getServiceInfoList)
                        .orElse(List.of());
                if (jdAfterSaleServices.isEmpty()) {
                    break;
                }
                //do something with jdAfterSaleServices
                Set<JdAfterSale> jdAfterSales = new ConcurrentSkipListSet<>(Comparator.comparing(JdAfterSale::getAfsServiceId));
                jdAfterSaleServices.parallelStream().forEach(j -> {
                    if (jdAfterSaleService.lambdaQuery().eq(JdAfterSale::getAfsServiceId, j.getAfsServiceId())
                            .eq(JdAfterSale::getDelFlag, Boolean.FALSE)
                            .exists()) {
                        return;
                    }
                    final JdAfterSale jdAfterSale = BeanCopyUtil.copy(j, JdAfterSale.class);
                    jdAfterSales.add(jdAfterSale);
                    //jdAfterSale.setCreateDate(r.getCreateDate().getTime());
                    //jdAfterSale.setUpdateDate(r.getUpdateDate().getTime());
                    jdAfterSale.setCreateBy("timing_job");
                    jdAfterSale.setUpdateBy("timing_job");
                    final LocalDateTime current = LocalDateTime.now();
                    jdAfterSale.setCreateTime(current);
                    jdAfterSale.setUpdateTime(current);
                });
                jdAfterSaleService.saveBatch(jdAfterSales);
                log.info("拉取jdAfterSaleService京东售后订单信息成功，共{}条, 拉取京东售后订单信息成功: {}", jdAfterSales.size(), JSONArray.toJSONString(jdAfterSales));
                pageNo++;
            }
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }


    private static LocalDate getFirstDayOfMonth(String dateMonthString) {

        // 定义日期格式
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM");

        // 解析字符串为 YearMonth
        YearMonth yearMonth = YearMonth.parse(dateMonthString, formatter);

        // 获取该月的最后一天并转换为 LocalDate
        return yearMonth.atEndOfMonth();
    }

}
